package com.tsfyun.scm.service.impl.finance;

import cn.hutool.core.lang.Snowflake;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tsfyun.common.base.enums.SerialNumberTypeEnum;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.SettlementAccountStatusEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.scm.dto.finance.SettlementAccountDTO;
import com.tsfyun.scm.dto.finance.SettlementAccountQTO;
import com.tsfyun.scm.entity.finance.SettlementAccount;
import com.tsfyun.scm.entity.finance.SettlementAccountMember;
import com.tsfyun.scm.entity.system.Subject;
import com.tsfyun.scm.entity.system.SubjectBank;
import com.tsfyun.scm.entity.system.SubjectOverseas;
import com.tsfyun.scm.entity.system.SubjectOverseasBank;
import com.tsfyun.scm.enums.SubjectTypeEnum;
import com.tsfyun.scm.mapper.finance.SettlementAccountMapper;
import com.tsfyun.scm.service.finance.IOverseasReceivingAccountService;
import com.tsfyun.scm.service.finance.ISettlementAccountMemberService;
import com.tsfyun.scm.service.finance.ISettlementAccountService;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.order.IExpOrderService;
import com.tsfyun.scm.service.system.*;
import com.tsfyun.scm.vo.finance.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 结汇主单 服务实现类
 * </p>
 *
 *
 * @since 2021-10-14
 */
@Service
public class SettlementAccountServiceImpl extends ServiceImpl<SettlementAccount> implements ISettlementAccountService {

    @Autowired
    private SettlementAccountMapper settlementAccountMapper;
    @Autowired
    private ISettlementAccountMemberService settlementAccountMemberService;
    @Autowired
    private IOverseasReceivingAccountService overseasReceivingAccountService;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private ISubjectOverseasService subjectOverseasService;
    @Autowired
    private ISubjectOverseasBankService subjectOverseasBankService;
    @Autowired
    private ISubjectService subjectService;
    @Autowired
    private ISubjectBankService subjectBankService;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private IExpOrderService expOrderService;

    @Override
    public PageInfo<SettlementAccountVO> pageList(SettlementAccountQTO qto) {
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        List<SettlementAccountVO> list = settlementAccountMapper.list(qto);
        return new PageInfo<>(list);
    }

    @Override
    public SettlementAccountDetailVO detail(Long id, String operation) {
        SettlementAccount settlementAccount = super.getById(id);
        Optional.ofNullable(settlementAccount).orElseThrow(()->new ServiceException("结汇单不存在"));
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation),settlementAccount.getStatusId());
        // 查询收款单信息
        List<OverseasReceivingAccountVO> overseasReceivingAccountList = settlementAccountMemberService.findOverseasReceivingAccount(id);
        SettlementAccountDetailVO vo = new SettlementAccountDetailVO();
        vo.setSettlementAccount(beanMapper.map(settlementAccount,SettlementAccountVO.class));
        vo.setOverseasReceivingAccountList(overseasReceivingAccountList);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSettlement(SettlementAccountDTO dto) {
        SubjectTypeEnum subjectType = SubjectTypeEnum.of(dto.getSubjectType());
        SettlementAccount settlementAccount = new SettlementAccount();
        settlementAccount.setSubjectId(dto.getSubjectId());
        switch (subjectType){
            case ABROAD:
                // 境外主体
                SubjectOverseas subjectOverseas = subjectOverseasService.getById(settlementAccount.getSubjectId());
                Optional.ofNullable(subjectOverseas).orElseThrow(()->new ServiceException("境外结汇主体不存在"));
                settlementAccount.setSubjectName(subjectOverseas.getName());
                // 境外银行
                SubjectOverseasBank subjectOverseasBank = subjectOverseasBankService.getById(dto.getSubjectBank());
                Optional.ofNullable(subjectOverseasBank).orElseThrow(()->new ServiceException("境外结汇银行不存在"));
                settlementAccount.setSubjectBank(subjectOverseasBank.getName());
                settlementAccount.setSubjectBankNo(subjectOverseasBank.getAccount());
                break;
            case MAINLAND:
                // 境内主体
                Subject subject = subjectService.getById(settlementAccount.getSubjectId());
                Optional.ofNullable(subject).orElseThrow(()->new ServiceException("境内结汇主体不存在"));
                settlementAccount.setSubjectName(subject.getName());
                // 境内银行
                SubjectBank subjectBank = subjectBankService.getById(dto.getSubjectBank());
                Optional.ofNullable(subjectBank).orElseThrow(()->new ServiceException("境内结汇银行不存在"));
                settlementAccount.setSubjectBank(subjectBank.getName());
                settlementAccount.setSubjectBankNo(subjectBank.getAccount());
                break;

            default:
                throw new ServiceException("结汇主体类型错误");
        }
        settlementAccount.setSubjectType(subjectType.getCode());
        settlementAccount.setMemo(dto.getMemo());
        // 根据收款单初始化结汇单
        SettlementPlusVO settlementPlusVO = overseasReceivingAccountService.initSettlement(StringUtils.stringToLongs(dto.getAccountId()));
        SettlementAccountVO settlementAccountVO = settlementPlusVO.getSettlementAccount();
        // 原币金额
        settlementAccount.setAccountValue(settlementAccountVO.getAccountValue());
        settlementAccount.setCurrencyId(settlementAccountVO.getCurrencyId());
        settlementAccount.setCurrencyName(settlementAccountVO.getCurrencyName());
        // 收款单号
        settlementAccount.setAccountNo(settlementAccountVO.getAccountNo());
        // 计算本币金额
        if(dto.getCustomsRate().compareTo(BigDecimal.ZERO) != 1){
            throw new ServiceException("结汇汇率必须大于零");
        }
        settlementAccount.setSettlementDate(dto.getSettlementDate());
        settlementAccount.setCustomsRate(dto.getCustomsRate());
        settlementAccount.setSettlementValue(settlementAccount.getAccountValue().multiply(settlementAccount.getCustomsRate()).setScale(2,BigDecimal.ROUND_HALF_UP));
        // 分摊本币金额到收款单
        List<OverseasReceivingAccountVO> overseasReceivingAccountVOList = settlementPlusVO.getOverseasReceivingAccountList();
        BigDecimal totalSettlementValue = BigDecimal.ZERO;
        for(OverseasReceivingAccountVO oraVO : overseasReceivingAccountVOList){
            oraVO.setSettlementValue(oraVO.getActualValue().divide(settlementAccount.getAccountValue(),20,BigDecimal.ROUND_HALF_UP).multiply(settlementAccount.getSettlementValue()).setScale(2,BigDecimal.ROUND_HALF_UP));
            totalSettlementValue = totalSettlementValue.add(oraVO.getSettlementValue()).setScale(2,BigDecimal.ROUND_HALF_UP);
        }
        // 防止分摊不均匀
        overseasReceivingAccountVOList.get(0).setSettlementValue(
                overseasReceivingAccountVOList.get(0).getSettlementValue().add(settlementAccount.getSettlementValue().subtract(totalSettlementValue)).setScale(2,BigDecimal.ROUND_HALF_UP)
        );
        // 获取收款单订单
        List<OverseasReceivingOrderVO> overseasReceivingOrderVOS = overseasReceivingAccountService.overseasReceivings(Arrays.asList(settlementAccount.getAccountNo().split(",")));
        List<String> orderNos = overseasReceivingOrderVOS.stream().map(OverseasReceivingOrderVO::getOrderNo).collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
        settlementAccount.setOrderNo(String.join(",",orderNos));
        List<String> clientNos = expOrderService.findClientNoByDocNos(orderNos);
        settlementAccount.setClientNo(String.join(",",clientNos));
        String docNo = serialNumberService.generateDocNo(SerialNumberTypeEnum.SETTLEMENT_ACCOUNT);
        settlementAccount.setDocNo(docNo);
        settlementAccount.setStatusId(SettlementAccountStatusEnum.COMPLETE.getCode());
        try {
            super.save(settlementAccount);
        } catch (DuplicateKeyException e) {
            if (e.getMessage().contains("UK_settlement_account_docNo")) {
                throw new ServiceException("结汇单单号已经存在，请稍后重试");
            } else {
                throw new ServiceException("保存失败，请检查填写数据是否合法");
            }
        }
        // 结汇单明细
        List<SettlementAccountMember> settlementAccountMemberList = new ArrayList<>();
        for(OverseasReceivingAccountVO oraVO : overseasReceivingAccountVOList){
            SettlementAccountMember sam = new SettlementAccountMember();
            sam.setId(snowflake.nextId());
            sam.setSettlementId(settlementAccount.getId());
            sam.setAccountId(oraVO.getId());
            sam.setAccountNo(oraVO.getDocNo());
            sam.setSettlementValue(oraVO.getSettlementValue());
            settlementAccountMemberList.add(sam);
            // 结汇人民币金额写入收款单
            overseasReceivingAccountService.writeSettlementValue(
                    sam.getAccountId(),settlementAccount.getDocNo(),oraVO.getSettlementValue(),
                    settlementAccount.getId(),settlementAccount.getCustomsRate()
            );
        }
        settlementAccountMemberService.savaBatch(settlementAccountMemberList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeSettlement(Long id) {
        SettlementAccount settlementAccount = super.getById(id);
        Optional.ofNullable(settlementAccount).orElseThrow(()->new ServiceException("结汇单不存在"));
        // 获取结汇明细
        List<SettlementAccountMember> memberList = settlementAccountMemberService.findByAccountId(settlementAccount.getId());
        memberList.forEach(member ->{
            overseasReceivingAccountService.canWriteSettlementValue(member.getAccountId(),member.getSettlementValue());
            settlementAccountMemberService.removeById(member.getId());
        });
        super.removeById(settlementAccount.getId());
    }

    @Override
    public SettlementAccount findByOverseasReceivingAccountId(Long accountId) {
        return settlementAccountMapper.findByOverseasReceivingAccountId(accountId);
    }
}
